package com.haha.webSocket;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.haha.webSocket.core.common.BotEventType;
import com.haha.webSocket.core.common.BotIntent;
import com.haha.webSocket.core.entity.Payload;
import com.haha.webSocket.core.entity.User;
import com.haha.webSocket.core.entity.dateCollect.AuthenticationDate;
import com.haha.webSocket.core.entity.dateCollect.ChannelDate;
import com.haha.webSocket.core.entity.dateCollect.GuildMessageDate;
import com.haha.webSocket.core.entity.dateCollect.HelloDate;
import com.haha.webSocket.core.entity.dateCollect.ReadyDate;
import com.haha.webSocket.core.enums.Opcode;
import jakarta.websocket.ClientEndpoint;
import jakarta.websocket.CloseReason;
import jakarta.websocket.DeploymentException;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import lombok.SneakyThrows;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

@ClientEndpoint
public class MyWebSocketClientEndpoint {

    private Session session;

    private Bot bot;

    private ObjectMapper objectMapper;

    private BotHttpClient botHttpClient;

    private String uri;

    @OnOpen
    public void onOpen(Session session) throws IOException, InterruptedException {
        this.objectMapper = new ObjectMapper();
        System.out.println("Connected to server");
        String appId = "";
        String appKey = "";
        String baseUrl = "https://api.sgroup.qq.com";
        String token = "Bot " + appId + "." + appKey;
        this.bot = new Bot(appId, appKey, baseUrl, token);
        this.botHttpClient = new BotHttpClient(bot);
        this.session = session;
        this.uri = "wss://api.sgroup.qq.com/websocket";
    }

    @OnMessage
    public void onMessage(String message, Session session) throws Exception {
        System.out.println("Message from server: " + message);
        this.session = session;

        //解析消息
//        Map<String, Object> payload = new JSONObject(message.getPayload()).toBean(Map.class);
        Payload payload = objectMapper.readValue(message, Payload.class);
        JsonNode jsonNode = objectMapper.readTree(message);

        JsonNode jsonNodeD = jsonNode.get("d");
        payload.setD(jsonNodeD);

        Opcode opcode = Opcode.getByCode(payload.getOp());

        switch (opcode) {
            case HELLO:
                HelloDate helloDate = objectMapper.treeToValue(
                        jsonNodeD, HelloDate.class);
                payload.setD(helloDate);
                helloOperateHandler(payload);
                break;
            case DISPATCH:
                dispatchOperateHandler(payload);
                break;
            case HEARTBEAT_ACK:
                heartbeatAckOperateHandler();
                break;
            case RECONNECT:
                reconnectionOperateHandler();
                break;
            case INVALID:
                System.err.println("\n\n" +
                        " .----------------.  .----------------.  .----------------.  .----------------.  .----------------. \n" +
                        "| .--------------. || .--------------. || .--------------. || .--------------. || .--------------. |\n" +
                        "| |  _________   | || |  _______     | || |  _______     | || |     ____     | || |  _______     | |\n" +
                        "| | |_   ___  |  | || | |_   __ \\    | || | |_   __ \\    | || |   .'    `.   | || | |_   __ \\    | |\n" +
                        "| |   | |_  \\_|  | || |   | |__) |   | || |   | |__) |   | || |  /  .--.  \\  | || |   | |__) |   | |\n" +
                        "| |   |  _|  _   | || |   |  __ /    | || |   |  __ /    | || |  | |    | |  | || |   |  __ /    | |\n" +
                        "| |  _| |___/ |  | || |  _| |  \\ \\_  | || |  _| |  \\ \\_  | || |  \\  `--'  /  | || |  _| |  \\ \\_  | |\n" +
                        "| | |_________|  | || | |____| |___| | || | |____| |___| | || |   `.____.'   | || | |____| |___| | |\n" +
                        "| |              | || |              | || |              | || |              | || |              | |\n" +
                        "| '--------------' || '--------------' || '--------------' || '--------------' || '--------------' |\n" +
                        " '----------------'  '----------------'  '----------------'  '----------------'  '----------------' \n");
                System.err.println("鉴权失败， 参数错误");
                System.exit(1);
            default:
                System.err.println("不识别的指令");
                break;
        }

    }

    private void heartbeatAckOperateHandler() {
        System.out.println("收到心跳回复");
    }

    @OnError
    public void onError(Session session, Throwable throwable) throws Exception {
        //异常处理
        System.out.println("异常处理");
        System.err.println("Error occurred:");
        throwable.printStackTrace();
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) throws Exception {
        //连接关闭
        System.out.println("服务端通知客户端重连1");
        this.bot.setIsLogin(false);
        System.out.println("Connection closed: " + closeReason.getReasonPhrase());
        reconnection();
    }

    private void reconnection() throws URISyntaxException, DeploymentException, IOException {
        System.out.println("服务端通知客户端重连2");
        Main.reconnection();
    }


    /**
     * 事件调度
     *
     * @param payload
     */
    private void dispatchOperateHandler(Payload payload) throws JsonProcessingException {
        Integer seq = payload.getS();
//        if (seq != null && seq > 0) {
//            this.bot.setSeq(seq);
//        }
        JsonNode jsonNodeD = (JsonNode) payload.getD();
        String event = payload.getT();
        switch (event) {
            case "READY": {
                ReadyDate d = objectMapper.treeToValue(jsonNodeD, ReadyDate.class);
                User user = d.getUser();

                this.bot.setBotId(user.getId());
                this.bot.setBotName(user.getUsername());
                this.bot.setBotSessionId(d.getSessionId());

                if (this.bot.getIsLogin()) {
                    handlerHeartbeatInterval();
                }
                this.bot.setIsLogin(true);
            }
            break;
            case BotEventType.EVENT_AT_MESSAGE_CREATE: {
                System.out.println("AT_MESSAGE_CREATE :: {}" + payload.getD().toString());
                GuildMessageDate messageDate = objectMapper.treeToValue(jsonNodeD, GuildMessageDate.class);
//                BotMessage botMessage = new BotMessage();
//                botMessage.setBot(bot);
//                botMessage.setMessageDate(messageDate);
//                botMessage.setPayload(payload);
//                eventPublisher.publishEvent(botMessage);

                String replyContent = "请问有什么可以帮助你？";

                String content = messageDate.getContent();
                String msgId = messageDate.getId();
                String channelId = messageDate.getChannelId();

                if (content.indexOf(" /") > 0) {
                    content = content.substring(content.indexOf(" /") + 2).trim();
                }

                System.out.println("messageId ::::::{}" + msgId);
                Map<String, Object> map = new HashMap<>();
                map.put("content", replyContent);
                map.put("msg_id", msgId);
                ObjectMapper objectMapper = new ObjectMapper();
                try {
                    String body = objectMapper.writeValueAsString(map);
                    String s = botHttpClient.sendPost(String.format("/channels/%s/messages", channelId), body).body();
                    System.out.println("s :::::: {}" + s);
                } catch (JsonProcessingException e) {
                    System.err.println(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
            break;
            case BotEventType.EVENT_CHANNEL_CREATE:
            case BotEventType.EVENT_CHANNEL_UPDATE:
            case BotEventType.EVENT_CHANNEL_DELETE:
                ChannelDate channelDate = objectMapper.treeToValue(jsonNodeD, ChannelDate.class);
                System.out.println("子频道事件：{}" + objectMapper.writeValueAsString(channelDate));
                break;
            default:
                System.err.println("不识别的事件类型");
                break;
        }
    }

    public boolean supportsPartialMessages() {
        //是否支持接收不完整的消息
        return false;
    }

    private void helloOperateHandler(Payload payload) {
        // 初始化心跳周期
        initHeartbeatInterval(payload);
        // 初始化频道列表
//        initGuildList();
        // 发送Socket授权信息
        sendSocketAuthInfo();
    }

    @SneakyThrows
    private void sendSocketAuthInfo() {

        synchronized (this.session) {
            Payload payload = new Payload();
            payload.setOp(Opcode.IDENTIFY.getCode());
            AuthenticationDate authenticationDate = new AuthenticationDate();
            //拼接监听的消息类型
            authenticationDate.setIntents(BotIntent.computeIntents(BotIntent.IntentPublicGuildMessage, BotIntent.IntentGuilds));
            authenticationDate.setToken("Bot 102060483.hygG0AnAGlce52JvoUvh7x8bbYrYfKI9");
            authenticationDate.setShard(new Integer[]{0,1});
            payload.setD(authenticationDate);
            String text = objectMapper.writeValueAsString(payload);
            System.out.println("发送的消息：" + text);
            this.session.getBasicRemote().sendText(text);
        }
    }

    /**
     * 处理周期心跳
     */
    private void handlerHeartbeatInterval() {
        Thread heartbeatThread = new Thread(() -> {
            while (true) {

                Map<String, Object> map = new HashMap<>();
                map.put("op", 1);
                map.put("d", "1");

                try {
                    Thread.sleep(this.bot.getHeartbeatInterval());
                    if (this.bot.getIsLogin()) {
                        synchronized (this.session) {
                            this.session.getBasicRemote().sendText(objectMapper.writeValueAsString(map));
                        }
                    }
                } catch (Exception e) {
                    System.err.println(e);
                }
            }
        });
        heartbeatThread.setDaemon(true);
        heartbeatThread.setName("HeartbeatThread");
        heartbeatThread.start();
    }

    /**
     * 服务端通知客户端重连
     */
    private void reconnectionOperateHandler() throws DeploymentException, URISyntaxException, IOException {
        System.out.println("服务端通知客户端重连");
        this.bot.setIsLogin(false);
//        eventPublisher.publishEvent(ActionEvent.builder()
//                .actionType(ActionType.RECONNECTION)
//                .build());
        reconnection();
    }

    /**
     * 初始化心跳周期
     *
     * @param payload
     */
    private void initHeartbeatInterval(Payload payload) {
        System.out.println("initHeartbeatInterval ::: {} " + payload);
        Long interval = ((HelloDate) payload.getD()).getHeartbeatInterval();
        System.out.println("heartbeat_time:" + interval);
        long heartbeatInterval = new BigDecimal(interval)
                .divide(new BigDecimal(2), 0, RoundingMode.HALF_DOWN)
                .longValue();
        System.out.println("heartbeat_interval:" + heartbeatInterval);
        this.bot.setHeartbeatInterval(heartbeatInterval);

    }

}
